using System.Net;
using System.Text.Json;
using MultiAgentsClient.ChatDataStructures;
using MultiAgentsClient.Manager.DataModel;
using MultiAgentsClient.Service.SignalRService;
using MultiAgentsShared;
using static MultiAgentsClient.Manager.MessageManager;

namespace MultiAgentsClient.Manager;

public class MessageWebviewOperator : ManagerBase
{
    private readonly SignalRChatService signalRChatService;

    private readonly SystemConfigDataModel systemConfigDataModel;
    private readonly UserDataModel userDataModel;

    public int MaxMessagesId { get; set; } = 0;
    private readonly SemaphoreSlim semaphore = new(1, 1);

    #region events
    // recieve command from UI
    public record MessageWebViewReceiveMessageCommand(object Sender, string Message);

    // recieve command from signalR
    public record GetFileContentResponseCommand(int FileId, string FileType, string FileContent);
    public record SwitchChatRoomBranchResponseCommand(int ChatRoomId, int MessageId);
    
    // send command to UI
    public record LoadMessageWebviewPageCommand(string Path);
    public record ExecuteMessageWebviewCommand(string JSCode);
    
    #endregion

    public MessageWebviewOperator(
        SignalRChatService signalRChatService,
        SystemConfigDataModel systemConfigDataModel,
        UserDataModel userDataModel)
    {
        this.signalRChatService = signalRChatService;
        this.systemConfigDataModel = systemConfigDataModel;
        this.userDataModel = userDataModel;

        eventBus.Subscribe<MessageWebViewReceiveMessageCommand>(cmd => { Task.Run(() => MessageWebViewReceiveMessageCommandHandler(cmd)); });
        eventBus.Subscribe<GetFileContentResponseCommand>(cmd => { Task.Run(() => GetFileContentApiResponseCommandHandler(cmd)); });
        eventBus.Subscribe<SwitchChatRoomBranchResponseCommand>(cmd => { Task.Run(() => SwitchChatRoomBranchResponseCommandHandler(cmd)); });
    }

    public async Task MessageWebViewReceiveMessageCommandHandler(MessageWebViewReceiveMessageCommand cmd)
    {
        string message = cmd.Message;
        JsonDocument doc = JsonDocument.Parse(message);
        JsonElement root = doc.RootElement;
        string? messageType = root.GetProperty("messageType").GetString();
        
        switch (messageType)
        {
            case "getFileContentById":
                {
                    int fileId = root.GetProperty("params").GetProperty("fileId").GetInt32();
                    await GetFileContent(fileId);
                    break;
                }
            case "resendMessage":
                {
                    int messageId = root.GetProperty("params").GetProperty("messageId").GetInt32();
                    string content = root.GetProperty("params").GetProperty("content").GetString() ?? "";
                    ResendMessage(messageId, content);
                    break;
                }
            case "overrideMessage":
                {
                    int messageId = root.GetProperty("params").GetProperty("messageId").GetInt32();
                    string content = root.GetProperty("params").GetProperty("content").GetString() ?? throw Debug.ShouldNotHappen();
                    await OverrideMessage(messageId, content);
                    break;
                }
            case "switchBranch":
                {
                    int messageId = root.GetProperty("params").GetProperty("messageId").GetInt32();
                    await SwitchChatRoomBranch(messageId);
                    break;
                }
            default:
                break;
        }
    }

    private async Task GetFileContent(int fileId)
    {
        await signalRChatService.SendSignalRRequest(SignalRChatService.ApiRequestType.GetFileContentApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "imageId", fileId },
            });
    }

    private async Task SwitchChatRoomBranch(int messageId)
    {
        await signalRChatService.SendSignalRRequest(SignalRChatService.ApiRequestType.SwitchChatRoomBranchApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "chatRoomId", userDataModel.selectedChatRoomId },
                { "messageId", messageId },
            });
    }

    private void ResendMessage(int messageId, string content)
    {
        eventBus.Publish(new ResendUserMessageCommand(messageId, content));
    }

    private async Task OverrideMessage(int messageId, string content)
    {
        await signalRChatService.SendSignalRRequest(SignalRChatService.ApiRequestType.OverrideMessageApiRequest.ToString(), new Dictionary<string, object>()
            {
                { "sessionId", systemConfigDataModel.sessionId },
                { "userId", userDataModel.Id },
                { "messageId", messageId },
                { "content", content },
            });
    }

    public async Task GetFileContentApiResponseCommandHandler(GetFileContentResponseCommand cmd)
    {
        await semaphore.WaitAsync();
        try
        {
            string content = JsonSerializer.Serialize(cmd.FileContent)[1..^1];
            string jsCode = $@"window.fileContent({{
                id: '{cmd.FileId}',
                type: '{cmd.FileType}',
                content: '{content}'
            }});";

            eventBus.Publish(new ExecuteMessageWebviewCommand(jsCode));
        }
        finally
        {
            semaphore.Release();
        }
    }

    public async Task SwitchChatRoomBranchResponseCommandHandler(SwitchChatRoomBranchResponseCommand cmd)
    {
        await semaphore.WaitAsync();
        try
        {
            if (userDataModel.selectedChatRoomId != cmd.ChatRoomId)
            {
                return;
            }

            string jsCode = $"refreshDisplayMessages({cmd.MessageId});";

            eventBus.Publish(new ExecuteMessageWebviewCommand(jsCode));
        }
        finally
        {
            semaphore.Release();
        }
    }

    private static string CreateAddMessageCommand(ChatMessageDisp message)
    {
        string mes;

        if (message.ToolCalls != null && message.ToolCalls.Count > 0)
        {
            mes = "";
            foreach (var toolMes in message.ToolCalls)
            {
                mes += message.Content + "\r\r";
                mes += toolMes.FunctionCall?.Name ?? throw Debug.ShouldNotHappen();
                mes += "\r";
                mes += toolMes.FunctionCall.Arguments;
                if (toolMes != message.ToolCalls.Last())
                {
                    mes += "\r";
                }
            }
            mes = JsonSerializer.Serialize(mes)[1..^1];
        }
        else
        {
            mes = JsonSerializer.Serialize(message.Content)[1..^1];
        }

        string historyMessagesJsArray = message.HistoryMessagesIdList != null
            ? $"[{string.Join(",", message.HistoryMessagesIdList)}]"
            : "[]";
        string jsCode = $"addMessage({{Id: {message.Id}, MsgOwner: '{message.MsgOwner}', Author: '{WebUtility.HtmlEncode(message.Author)}', Content: '{mes}', History: {historyMessagesJsArray}}});";
        return jsCode;
    }

    public void LoadMessages(List<ChatMessageDisp> chatMessages, int lastMessageId)
    {
        chatMessages.Reverse();

        string jsCode = "clearMessage();";
        foreach (ChatMessageDisp chatMessage in chatMessages)
        {
            jsCode += CreateAddMessageCommand(chatMessage);
        }

        if (lastMessageId == 0 && chatMessages.Count > 0)
        {
            lastMessageId = chatMessages.Last().Id;
        }

        jsCode += $"refreshDisplayMessages({lastMessageId});";

        eventBus.Publish(new ExecuteMessageWebviewCommand(jsCode));
    }

    public void AddMessage(ChatMessageDisp message)
    {
        string jsCode = CreateAddMessageCommand(message);
        jsCode += $"refreshDisplayMessages({message.Id});";

        MaxMessagesId = message.Id;

        eventBus.Publish(new ExecuteMessageWebviewCommand(jsCode));
    }

    public async Task UpdateMessageAsync(ChatMessageDisp message)
    {
        await semaphore.WaitAsync();
        try
        {
            string messageType = message.MsgOwner;
            string mes;

            if (message.ToolCalls != null && message.ToolCalls.Count > 0)
            {
                mes = "";
                foreach (var toolMes in message.ToolCalls)
                {
                    mes += message.Content + "\r\r";
                    mes += toolMes.FunctionCall?.Name ?? throw Debug.ShouldNotHappen();
                    mes += "\r";
                    mes += toolMes.FunctionCall.Arguments;
                    if (toolMes != message.ToolCalls.Last())
                    {
                        mes += "\r";
                    }
                }
                mes = JsonSerializer.Serialize(mes)[1..^1];
            }
            else
            {
                mes = JsonSerializer.Serialize(message.Content)[1..^1];
            }

            string historyMessagesJsArray = message.HistoryMessagesIdList != null
            ? $"[{string.Join(",", message.HistoryMessagesIdList)}]"
            : "[]";

            string jsCode = $@"updateMessage({{
                Id: {message.Id},
                MsgOwner: '{messageType}',
                Content: '{mes}',
                Author: '{WebUtility.HtmlEncode(message.Author)}',
                History: {historyMessagesJsArray}
            }});";

            eventBus.Publish(new ExecuteMessageWebviewCommand(jsCode));
        }
        finally
        {
            semaphore.Release();
        }
    }
}
